package com.chaoxinet.csmall.passport.service.impl;

import com.alibaba.fastjson.JSON;
import com.chaoxinet.csmall.passport.exception.ServiceException;
import com.chaoxinet.csmall.passport.mapper.AdminMapper;
import com.chaoxinet.csmall.passport.mapper.AdminRoleMapper;
import com.chaoxinet.csmall.passport.pojo.dto.AdminAddNewDTO;
import com.chaoxinet.csmall.passport.pojo.dto.AdminLoginDTO;
import com.chaoxinet.csmall.passport.pojo.entity.Admin;
import com.chaoxinet.csmall.passport.pojo.entity.AdminRole;
import com.chaoxinet.csmall.passport.pojo.vo.AdminListItemVO;
import com.chaoxinet.csmall.passport.pojo.vo.AdminStandardVO;
import com.chaoxinet.csmall.passport.security.AdminDetails;
import com.chaoxinet.csmall.passport.service.AdminService;
import com.chaoxinet.csmall.passport.web.ServiceCode;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

/**
 * 品牌业务实现
 *
 * @author java@tedu.cn
 * @version 0.0.1
 */
@Slf4j
@Service
public class AdminServiceImpl implements AdminService {
    // 在配置文件中配置了secretKey
    @Value("${camall.jwt.secret-key}")
    private String secretKey;
    @Value("${camall.jwt.duration-in-minutes}")
    private long durationInMinutes;

    @Autowired
    private PasswordEncoder password;

    @Autowired
    private AdminMapper adminMapper;

    @Autowired
    private AdminRoleMapper adminRoleMapper;

    @Autowired
    private AuthenticationManager authenticationManager;

    public AdminServiceImpl() {
        log.debug("创建业务逻辑对象：AdminServiceImpl");
    }

    /**
     * 登录管理员
     *
     * @param adminLoginDTO 管理员登录信息
     */
    @Override
    public String login(AdminLoginDTO adminLoginDTO) {
        log.debug("开始处理登录业务!");
        Authentication authentication =
                new UsernamePasswordAuthenticationToken(
                        adminLoginDTO.getUsername(),
                        adminLoginDTO.getPassword()
                );
        Authentication authenticationResult =
                authenticationManager.authenticate(authentication);
        log.debug("认证通过，返回的认证信息;{}", authenticationResult);
        // 将通过认证的管理员的相关信息存入到JWT中
        // 准备生成JWT的相关数据
        Date date = new Date(System.currentTimeMillis() + durationInMinutes * 60 * 1000);
        AdminDetails principal = (AdminDetails) authenticationResult.getPrincipal();
        Map<String, Object> claimer = new HashMap<>();
        claimer.put("id", principal.getId());
        claimer.put("username", principal.getUsername());
        Collection<GrantedAuthority> authorities = principal.getAuthorities();
        String authoritiesJsonString = JSON.toJSONString(authorities);
        claimer.put("authoritiesJsonString", authoritiesJsonString);

        // 生成JWT
        String jwt = Jwts.builder()
                .setHeaderParam("alg", "HS256")  // 设置头部参数alg为HS256
                .setHeaderParam("typ", "JWT")    // 设置头部参数typ为JWT
                .setClaims(claimer)              // 设置声明（payload）为claimer
                .setExpiration(date)             // 设置过期时间为date
                .signWith(SignatureAlgorithm.HS256, secretKey)  // 使用HS256算法和密钥对JWT进行签名
                .compact();
        // 返回JWT
        return jwt;
    }

    /**
     * 添加新管理员
     *
     * @param adminAddNewDTO 管理员添加信息
     */
    @Override
    public void addNew(AdminAddNewDTO adminAddNewDTO) {
        log.debug("开始处理添加管理员的业务，参数：{}", adminAddNewDTO);

        // 检查客户端传来的角色id数组是否包含1号角色
        Long[] roleIds = adminAddNewDTO.getRoleIds();
        for (int i = 0; i < roleIds.length; i++) {
            if (roleIds[i] == 1){
                String message = "添加管理员失败，非法访问！";
                log.error(message);
                throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
            }
        }
        // 检查此用户名有没有被占用
        String username = adminAddNewDTO.getUsername();
        int count = adminMapper.countByUsername(username);
        if (count > 0) {
            String message = "添加管理员失败，用户名【" + username + "】已经被占用！";
            log.error(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }
        // 对数据中的密码进行加密处理
        String encodedPassword = password.encode(adminAddNewDTO.getPassword());
        adminAddNewDTO.setPassword(encodedPassword);
        log.debug("加密后的密码，参数：{}", adminAddNewDTO.getPassword());
        // 创建实体对象
        Admin admin = new Admin();
        // 将当前方法参数的值复制到实体类型的对象中
        BeanUtils.copyProperties(adminAddNewDTO, admin);
        // 补全属性值（不由客户端提交的属性的值，应该在插入之前补全）
        admin.setLoginCount(0);
        // 将管理员数据写入到数据库中
        log.debug("即将向管理员表中写入数据：{}", admin);
        int rows = adminMapper.insert(admin);
        if (rows != 1) {
            String message = "添加管理员失败，服务器忙，请稍后再次尝试！【错误码：1】";
            log.error(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
        // 向管理员与角色的关联表中插入关联数据
        AdminRole[] adminRoleList = new AdminRole[roleIds.length];
        for (int i = 0; i < adminRoleList.length; i++) {
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(admin.getId());
            adminRole.setRoleId(roleIds[i]);
            adminRoleList[i] = adminRole;
        }
        rows = adminRoleMapper.insertBatch(adminRoleList);
        if (rows != adminRoleList.length) {
            String message = "添加管理员失败，服务器忙，请稍后再次尝试！【错误码：2】";
            log.error(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
    }


    /**
     * 查询管理员列表
     *
     * @return 管理员列表
     */
    @Override
    public List<AdminListItemVO> list() {
        log.debug("开始处理查询管理员列表的业务");
        List<AdminListItemVO> list = adminMapper.list();
        // 获取迭代器对象，用于遍历列表
        Iterator<AdminListItemVO> iterator = list.iterator();
        // 遍历列表中的元素
        while (iterator.hasNext()) {
            // 获取当前元素
            AdminListItemVO admin = iterator.next();
            // 判断当前元素的id是否为1
            if (admin.getId() == 1) {
                // 如果是，则移除当前元素
                iterator.remove();
            }
            break;
        }
        ;
        return list;
    }

    /**
     * 删除管理员
     *
     * @param id 管理员id
     */
    @Override
    public void delete(Long id) {
        log.debug("开始处理[根据id删除 管理员]的业务,参数:{}",id);
        // 不允许删除1号管理员
        if (id == 1) {
            String message = "删除管理员失败，非法访问！";
            log.error(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        // 检查尝试删除的数据是否存在
        Object queruResult = adminMapper.getStandardById(id);
        if (queruResult == null) {
            String message = "删除管理员失败，数据不存在！";
            log.error(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 执行删除管理员的操作
        log.debug("即将执行删除数据,参数:{}",id);
        int rows = adminMapper.deleteById(id);
        if (rows != 1){
            String message = "删除管理员失败，服务器忙，请稍后再次尝试！【错误码：1】";
            log.error(message);
            throw new ServiceException(ServiceCode.ERR_DELETE, message);
        }

        // 执行删除--管理员与角色的关联表数据
        rows = adminRoleMapper.deleteByAdminId(id);
        if (rows < 1){
            String message = "删除管理员失败，服务器忙，请稍后再次尝试！【错误码：2】";
            log.error(message);
            throw new ServiceException(ServiceCode.ERR_DELETE, message);
        }
    }

    /**
     * 启用类别
     *
     * @param id 类别id
     */
    @Override
    public void setEnable(Long id) {
        updateEnableById(id, 1);
    }

    /**
     * 禁用类别
     *
     * @param id 类别id
     */
    @Override
    public void setDisable(Long id) {
        updateEnableById(id, 0);
    }

    /**
     * 根据id更新类别的启用状态
     *
     * @param id     类别id
     * @param enable 启用状态，1为启用，0为禁用
     */
    private void updateEnableById(Long id, Integer enable) {
        String[] enableText = {"禁用","启用"};
        log.info("开始处理[{}的业务],ID:{},目标状态:{}",enableText[enable],id,enableText[enable]);
        // 调用Mapper对象的getStandardById()方法执行查询!
        AdminStandardVO queryResult = adminMapper.getStandardById(id);
        // 判断查询结果是否为null
        if (queryResult == null) {
            // 是:抛出异常(NOT_FOUND)
            String message = enableText[enable]+"处理类别失败!尝试访问的类别不存在";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 判断以上结果中的enable是否与参数相同
        if (queryResult.getEnable().equals(enable)) {
            // 是 :抛出异常(NOY_CONFLICT)
            String message = enableText[enable]+"类别失败!当前类别已是"+enableText[enable]+"状态";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }
        // 创建Category对象,并将两个参数的值封装起来
        Admin admin = new Admin();
        admin.setId(id);
        admin.setEnable(enable);
        // 调用Mapper的update()方法,执行更新!,并获取放回结果判断是否符合预期值
        int row = adminMapper.update(admin);
        if (row != 1) {
            String message = enableText[enable]+"类别处理失败!服务器忙,请稍后在尝试!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
    }
}
